package cn.xuqiudong.console.module.backup.handler;

import cn.xuqiudong.common.base.model.BaseResponse;
import cn.xuqiudong.common.base.vo.BooleanWithMsg;
import cn.xuqiudong.common.util.CnToSpellUtils;
import cn.xuqiudong.console.module.backup.helper.baidu.BaiduPanHelper;
import cn.xuqiudong.console.module.backup.model.BackupConfig;
import cn.xuqiudong.console.module.backup.model.BackupRecord;
import cn.xuqiudong.console.module.backup.service.BackupConfigService;
import cn.xuqiudong.console.module.backup.service.BackupRecordService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 描述: 备份处理器
 * @author Vic.xu
 * @since 2024-02-05 17:29
 */
@Component
public class BackupHandler {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private BaiduPanHelper baiduPanHelper;

    @Resource
    private BackupRecordService backupRecordService;

    @Resource
    private BackupConfigService backupConfigService;

    public BaseResponse<String> handle(Integer id) {
        if (id == null) {
            return BaseResponse.error("id is required");
        }
        BackupConfig config = backupConfigService.findById(id);
        if (config == null) {
            return BaseResponse.error("no backup configuration found by id " + id);
        }
        return handle(config);
    }


    /**
     * 处理文件： 查询出文件夹下最后上传时间之后发生修改的文件
     *  1. 原始文件夹下 对文件进行递归遍历
     *  2. 对文件进行上传
     *  3. 上传后把文件保持原来的目录转移到目标文件夹下 (有的需要处理， 有的不需要处理)
     */
    public BaseResponse<String> handle(BackupConfig config) {
        Date now = new Date();
        Date lastBackupTime = config.getLastBackupTime();
        List<Path> list = list(config.getDirPath(), lastBackupTime == null ? null : lastBackupTime.getTime());
        if (CollectionUtils.isEmpty(list)) {
            String msg = MessageFormat.format("{0}[{1}]自{2}后无变更文件需要备份", config.getName(), config.getDirPath(), config.getLastBackupTimeStr());
            logger.info(msg);
            return BaseResponse.success(msg);
        }
        logger.info("{}[{}]自{}后需要备份的文件数为:{}", config.getName(), config.getDirPath(), config.getLastBackupTimeStr(), list.size());
        int successfulNumber = 0;
        int failedNumber = 0;
        for (Path path : list) {
            boolean upload = upload(path, config);
            if (upload) {
                successfulNumber++;
            } else {
                failedNumber++;
            }
        }
        String msg = MessageFormat.format("根据配置[名称:{0}(分类:{1})]上传文件到百度云结果: 成功{2}条, 失败{3}条", config.getName(), config.getCategory(), successfulNumber, failedNumber);

        logger.info(msg);
        backupConfigService.updateLastBackupTime(config.getId(), now);
        return BaseResponse.success(msg);
    }


    public boolean upload(Path path, BackupConfig config) {
        Path rootPath = Paths.get(config.getDirPath());
        boolean success = false;
        //获取到相对路径
        Path relativize = rootPath.relativize(path);
        String fullSpellRelativizePath = CnToSpellUtils.getFullSpell(relativize.toString());
        fullSpellRelativizePath = config.obtainStorePrefix() + fullSpellRelativizePath;
        //开始上传
        BooleanWithMsg upload = baiduPanHelper.upload(fullSpellRelativizePath, path.toFile());
        BackupRecord record = new BackupRecord(config, path);

        //上传成功后判断是否需要转移原文件
        if (upload.isSuccess()) {
            success = true;
            if (Boolean.TRUE.equals(config.getNeedTransfer())) {
                BooleanWithMsg transfer = transfer(config.getTransferDir(), path, relativize);
                if (transfer.isSuccess()) {
                    //转移失败 不算失败
                    record.setTransferPath(transfer.getMessage());
                }
            }
        }

        record.setSuccessful(upload.isSuccess());
        record.setResult(upload.getMessage());
        backupRecordService.save(record);
        return success;
    }

    /**
     * 转移文件
     */
    @SuppressWarnings("ResultOfMethodCallIgnored")
    private BooleanWithMsg transfer(String transferDir, Path source, Path relativize) {
        try {
            logger.info("开始转移文件{}到{}目录下的{}", source, transferDir, relativize);
            File file = new File(transferDir, relativize.toString());
            if (file.exists()) {
                boolean delete = file.delete();
                if (!delete) {
                    logger.warn("delete file failed");
                }
            }
            FileUtils.moveFile(source.toFile(), file);
            return BooleanWithMsg.success(file.getAbsolutePath());
        } catch (IOException e) {
            logger.error("转移文件出错", e);
            return BooleanWithMsg.fail(e.getMessage());
        }

    }


    /**
     * 查询出文件夹下最后上传时间之后发生修改的文件
     * @param root 文件夹
     * @param lastBackupTime 最后备份时间
     * @return List<Path>
     */
    public List<Path> list(String root, Long lastBackupTime) {

        try (Stream<Path> walk = Files.walk(Paths.get(root))) {
            Stream<Path> pathStream = walk.filter(Files::isRegularFile);

            boolean needFilter = lastBackupTime != null && lastBackupTime > 0;
            if (needFilter) {
                pathStream = pathStream.filter(p -> {
                    //  Files.getLastModifiedTime(p);
                    long l = p.toFile().lastModified();
                    return lastBackupTime <= l;
                });
            }
            return pathStream.collect(Collectors.toList());
        } catch (IOException e) {
            logger.error("遍历文件树出错", e);
            return null;
        }
    }

    public static void main(String[] args) {
        String s = "";
        BigDecimal bigDecimal = new BigDecimal(s);
        System.out.println(bigDecimal);
    }


}
